﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.Security;
using BigfootDNN.Mvc;
using BigfootWeb;
using BigfootWeb.Helpers;
using DotNetNuke.Common;
using DotNetNuke.Entities.Modules;
using DotNetNuke.Services.Exceptions;

namespace BigfootDNN
{

    /// <summary>
    /// This is the heart your DNN module application. It contains your module informaiton, as well as the methods
    /// and helpers you may use throughout your application. It also is the main source for the StaticUrl MvcUrl and ControlUrl
    /// methods that the baseview classes inherit. It also has all the functions inherited from the BigfootWeb.WebApp 
    /// you may use in all ASP.NET applications.
    /// </summary>
    public class DnnApp : WebApp
    {
        /// <summary>
        /// This is the name of the assembly/dll that contains the controllers
        /// </summary>
        public string ModuleAssemblyName;

        /// <summary>
        /// The root namespace of the module where the controllers reside)
        /// </summary>
        public string ModuleNamespace;

        /// <summary>
        /// Name of the folder where the module resides inside the DesktopModules directory
        /// </summary>
        public string ModuleFolderName;

        /// <summary>
        /// This is the short name of your module. This is the name used in the CSS files as well as the root div element
        /// on the route user control for your mvc views
        /// </summary>
        public string ModuleShortName;

        /// <summary>
        /// The database object qualifier for this module
        /// </summary>
        public string ModuleDBObjectQualifier;

        /// <summary>
        /// Retreives the view that is rendered with the error information
        /// </summary>
        public string ErrorViewPath { get { return AppPath + "error.ascx"; } }


        /// <summary>
        /// Inititalizes the MVC App
        /// </summary>
        /// <param name="moduleAssemblyName">This is the name of the assembly/dll that contains the controllers</param>
        /// <param name="moduleNamesapce">The root namespace of the module where the controllers reside)</param>
        /// <param name="moduleFolderName">Name of the folder where the module resides inside the DesktopModules directory</param>
        /// <param name="moduleShortName">This is the short name of your module. This is the name used in the CSS files as well as the root div element on the route user control for your mvc views</param>
        /// <param name="moduleDBObjectQualifier">The database object qualifier for this module</param>
        public DnnApp(string moduleAssemblyName, string moduleNamesapce, string moduleFolderName, string moduleShortName, string moduleDBObjectQualifier) 
            : base("~/DesktopModules/" + moduleFolderName + "/", new ASPContext(), new DnnEmailProvider())
        {
            ModuleAssemblyName = moduleAssemblyName;
            ModuleNamespace = moduleNamesapce;
            ModuleFolderName = moduleFolderName;
            ModuleShortName = moduleShortName;
            ModuleDBObjectQualifier = moduleDBObjectQualifier;
        }
        
        /// <summary>
        /// This the login url properly configured to return to the home page
        /// </summary>
        public static string LoginUrl
        {
            get
            {
                var url = FormsAuthentication.LoginUrl;
                url += url.Contains("?") ? "&" : "?";
                url += "returnurl=" + Globals.NavigateURL();
                return url;
            }
        }

        /// <summary>
        /// The url for the portal's home page        /// </summary>
        public static string HomeUrl { get { return Globals.NavigateURL(); } }
        
        /// <summary>
        /// Provides the application with access to the DataProvider in order to run queries etc.
        /// </summary>
        public DataProvider DB
        {
            get { return _db ?? (_db = new DataProvider(ModuleDBObjectQualifier)); }
            set { _db = value; }
        }
        private DataProvider _db;
        
        ///// <summary>
        ///// This method must be called on every MVC request by the RouteHandlers
        ///// </summary>
        //public void InitMVC(string moduleFolderName)
        //{
        //    // Only initialize once
        //    if (_MVCInitialized) return;

        //    // Set the AppPath
        //    AppPath = "~/DesktopModules/" + moduleFolderName + "/";

        //    // Mark the MVC portion as initialized
        //    _MVCInitialized = true;
        //}
        //private static bool _MVCInitialized = false;
        
        ///// <summary>
        ///// Gets a browser usable path to a content file within the system. It uses the extion of the file to determine the location of the static file
        ///// within the content folder.
        ///// i.e. "image.jpg" will return "/desktopmodules/modulename/content/images/image.jpg"
        ///// i.e. "menu/left.jpg" will return "/desktopmodules/modulename/content/images/menu/left.jpg"
        ///// i.e. "test.js" will return "/desktopmodules/modulename/content/js/test.js"
        ///// </summary>
        ///// <param name="filename">The name of the file relative to the content folder i.e. left.jpg or menu/left.jpg</param>
        ///// <returns>A url that may be used from the browser to access the static resource</returns>
        //public string StaticUrl(string filename) { return StaticUrl(filename, false); }

        ///// <summary>
        ///// Gets a browser usable path to a content file within the system. It uses the extion of the file to determine the location of the static file
        ///// within the content folder.
        ///// i.e. "image.jpg" will return "/desktopmodules/modulename/content/images/image.jpg"
        ///// i.e. "menu/left.jpg" will return "/desktopmodules/modulename/content/images/menu/left.jpg"
        ///// i.e. "test.js" will return "/desktopmodules/modulename/content/js/test.js"
        ///// </summary>
        ///// <param name="filename">The name of the file relative to the content folder i.e. left.jpg or menu/left.jpg</param>
        ///// <param name="fullPath">Determines weather to include the domain name in the url. (i.e. http://domain.com/desktopmodules/modulename/content/image.jpg)</param>
        ///// <returns>A url that may be used from the browser</returns>
        //public string StaticUrl(string filename, bool fullPath) { return GetAbsoluteUrl(GetRelativeUrl(filename), fullPath) /*return DNN.GetClientUrl(GetRelativeUrl(filename))*/; }

        /// <summary>
        /// Gets a browser usable path to a MVC controller action.
        /// </summary>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="fullUrl">Determines weather to append the domain name to the url</param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser</returns>
        public string MvcUrl(string controller, string action, int portalId, int tabId, int moduleId, string querystring, bool fullUrl)
        {
            var relurl = Globals.NavigateURL(tabId, "", BuildUrlRoute(Params.QS.Route(moduleId), controller, action, portalId, tabId, moduleId, querystring).ToArray());
            if (fullUrl && relurl.StartsWith("http") == false)
            {
                relurl = GetAbsoluteUrl(relurl, true);
            }
            return relurl;
        }

        /// <summary>
        /// Gets a browser usable path to a MVC controller action for AJAX requests. It activates the ajax route handler.
        /// </summary>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser to make an ajax request</returns>
        public string MvcAjaxUrl(string controller, string action, int portalId, int tabId, int moduleId, string querystring)
        {
            var qs = BuildUrlRoute(Params.QS.AjaxRoute(ModuleShortName), controller, action, portalId, tabId, moduleId, querystring);
            var relurl = AppPath + "route.ashx?" + String.Join("&", qs.ToArray());
            if (relurl.StartsWith("~/"))
                relurl = relurl.Substring(2);
            if (relurl.EndsWith("/"))
                relurl = relurl.Substring(0, relurl.Length - 1);
            return GetApplicationPath() + relurl;
        }

        /// <summary>
        /// Receives the route data from the url and converts it into a RouteInfo object
        /// </summary>
        /// <param name="routeData">The route data from the url</param>
        public RouteInfo DecodeUrlRoute(string routeData)
        {
            var routeVars = routeData.Split('-');

            // Validate the route data parameter
            if (routeVars.Length != 5) throw new ApplicationException("Invalid route: " + routeData);

            var controller = routeVars[0];
            var action = routeVars[1];
            var tabId = int.Parse(routeVars[2]);
            var moduleId = int.Parse(routeVars[4]);
            var modData = new ModuleController().GetModule(moduleId, tabId);
            var route = new RouteInfo(controller, action, modData, this);
            return route;
        }

        /// <summary>
        /// Creates the Route data to be included in the URL Querystring. It inlcudes the parameter name.
        /// </summary>
        /// <param name="paramName">The name of the parameter to use in the querystring</param>
        /// <param name="controller">The name of the controller that contains the action</param>
        /// <param name="action">The name of the action within the controller</param>
        /// <param name="moduleId">The module id to include in the url route</param>
        /// <param name="querystring">Appends this value to the querystring </param>
        /// <param name="portalId">The portal id to include in the url route</param>
        /// <param name="tabId">The tab id to include in the url route</param>
        /// <returns>A url that may be used from the browser to make an ajax request</returns>
        private List<string> BuildUrlRoute(string paramName, string controller, string action, int portalId, int tabId, int moduleId, string querystring)
        {

            // Append any other routes currently in this page

            // Build the route data
            var routeData = string.Format("{0}-{1}-{2}-{3}-{4}",
                                           controller,
                                           action,
                                           tabId,
                                           portalId,
                                           moduleId);

            var qs = new List<string>();
            //qs.Add(routeData);
            const string paramFormat = "{0}={1}";

            // Add the route parameter
            qs.Add(string.Format(paramFormat, paramName, routeData));

            // Add the querystring parameters
            if (!string.IsNullOrEmpty(querystring))
            {
                foreach (var s in querystring.Split('&'))
                {
                    var qsItem = s.Split('=');
                    if (string.IsNullOrEmpty(s)) continue;
                    qs.Add(qsItem.Length > 1 ? string.Format(paramFormat, qsItem[0], qsItem[1]) :
                                               string.Format(paramFormat, s, ""));
                }
            }

            // Add any other route paramters currently in the request. When in AjaxRequest mode, although these are not used
            // it makes calls to the MvcUrl or AjaxUrl functions from views being executed within an Ajax Request keep their 
            // place for other modules within the page. Be it the same or another module also using BigfootMVC
            foreach (var qp in Request.QueryString.AllKeys)
            {
                if (!qp.ToLower().StartsWith("route_") || qp.Equals(paramName)) continue;
                
                var value = Request.QueryString[qp];
                qs.Add(string.Format(paramFormat, qp, value));
            }

            return qs;
        }

        /// <summary>
        /// This is a shortcut to DotNetNuke's NavigateUrl. It gets the url path to activate a certain DNN control within your module.
        /// </summary>
        /// <param name="moduleId">The ModuleID for the module containing the control</param>
        /// <param name="controlName">The control key within your module delcaration</param>
        /// <param name="queryString">Appends this value to the querystring </param>
        /// <returns>A url that may be used from the browser</returns>
        public string ControlUrl(int moduleId, string controlName, string queryString)
        {
            queryString += string.IsNullOrEmpty(queryString) ? "mid=" + moduleId : "&mid=" + moduleId;
            return Globals.NavigateURL(controlName, queryString);
        }
        
        /// <summary>
        /// This flags indicates that we have implicitly called response.end and it does not log the exception
        /// throws by premature response termination
        /// </summary>
        public bool ResponseEndCalled
        {
            get
            {
                return Context.Items.Contains("ResponseEndCalled") && (bool)Context.Items["ResponseEndCalled"];
            }
            set
            {
                if (Context.Items.Contains("ResponseEndCalled"))
                    Context.Items["ResponseEndCalled"] = value;
                else
                    Context.Items.Add("ResponseEndCalled", value);
            }
        }
        
        /// <summary>
        /// Handle an exception. It logs it to DNN and knows how to handle security exceptions on ajax requests etc.
        /// </summary>
        /// <param name="ex">The exception to handle</param>
        public void HandleException(Exception ex)
        {
            HandleException(ex, true);
        }

        /// <summary>
        /// Handle an exception. It logs it to DNN and knows how to handle security exceptions on ajax requests etc.
        /// </summary>
        /// <param name="ex">The exception to handle</param>
        /// <param name="ReThrow">Determines weather to rethrow the exception after the loging actions etc. have been executed</param>
        public void HandleException(Exception ex, bool ReThrow)
        {
            // Ignore exceptions due to response manually ended
            if (ResponseEndCalled) { return; }

            // Log the exception in DNN
            Exceptions.LogException(ex);

            // When an ajax request, signal to the client that the request failed.
            if (IsAjaxRequest)
            {
                Response.Clear();

                RenderViewToResponse(AppPath + "error.ascx", ErrorViewModel.Create(ex, Request.Url.ToString()));

                // Set the status code to 402 for security problems
                if ((ex.GetType().Name == typeof(System.Security.SecurityException).Name) || 
                    (ex.InnerException != null && object.ReferenceEquals(ex.InnerException.GetType().Name, typeof(System.Security.SecurityException).Name)))
                {
                    Response.StatusCode = 402;
                }
                else
                {
                    Response.StatusCode = 500;
                }

                Response.StatusDescription = ex.Message;
                Response.End();
            }
            else if (ReThrow)
            {
                throw ex;
            }
        }
        
        /// <summary>
        /// Outputs the appropriate Javascript and CSS references needed by BigfootMVC
        /// </summary>
        /// <returns>The html needed to include in the mvc page</returns>
        public string AddBigfootScriptAndCssDependencies()
        {
            /*
            <%= HtmlHelper.JSReference(Route.App.StaticUrl("req.js"))%>
            <%= HtmlHelper.CSSReference(Route.App.StaticUrl("BigfootWeb.css"))%>

            <%--EXECUTE SCRIPTS--%>
            <script type="text/javascript">    
                // Global variables
                var loginurl = "<%= Route.App.LoginUrl%>";

                // Load the scripts as needed
                Req.baseUrl = "<%= Route.App.StaticUrl(Route.App.ScriptsPath, true)%>";
                Req('bigfootWeb', function() { bigfootWeb.connectAjax(); } );
            </script>
             */
            var script = new StringBuilder();
            script.AppendLine(Html.JSReference(StaticUrl("req.js")));
            script.AppendLine(Html.CSSReference(StaticUrl("BigfootWeb.css")));
            script.AppendFormat(@"
                <script type='text/javascript'>    
                    var loginurl = '{0}';
                    Req.baseUrl = '{1}';
                    Req('bigfootWeb', function() {{ bigfootWeb.connectAjax(); }} );
                </script>",
                LoginUrl, 
                StaticUrl(ScriptsPath, true));

            // Return the script
            return script.ToString();
        }

    }
}
